'''
Created on 03.06.2013

@author: Cornelius
'''
from libavg import avg,AVGApp
from collections import OrderedDict
import homeScreen, lobbyScreen, user, field, playerStats
import sys,thread, threading

from twisted.internet import reactor
from twisted.python import log
 
from autobahn.websocket import WebSocketServerFactory, \
                               WebSocketServerProtocol, \
                               listenWS


class UbiBomb(AVGApp):
    
    minPlayer = 2
    minKickVotes = 2
    suddenDeathTimer = 120000;
    __size = (1280,720)
    #__size = (1920,1080)
    __topOffset = 1
    __fieldSize = (19, 15)
    __statsHeight = 0
    cmdQueue = []
    gamestate = 0 # 0=not started or finished
    statCounterValue = 10
    timer = None
    gamestarted = 0
    abortGame = 0
    inStats = 0
    
    def __init__(self):
        #initalize size, rootNode
        self.player=avg.Player.get()
        #self.player.setResolution(True, 1920, 1080, 32)
        self.canvas=self.player.createMainCanvas(size = self.__size)
        self.__rootNode=self.canvas.getRootNode()
        
        #initialise/create home and lobbyScreen
        self.home = homeScreen.HomeScreen(self.__size, self.__rootNode) ##create the HomeScreen, return a div-container
        self.lobbyscreen = lobbyScreen.LobbyScreen(self.__size, self.__rootNode)
        self.lobbyscreen.active=0
        
        #initialise WebSocket
        thread.start_new_thread(self.initializeWebSocket, ()) ##start the WebSocket in new Thread
        
        #framehandler to handle taskQueue in mainThread
        self.player.setOnFrameHandler(self.checkQueue)
        
        #thread.start_new_thread(self.waitForConnection, ()) ## change lobby and homescreen
    
    #execute methods from taskQueue    
    def checkQueue(self):
        if(len(self.cmdQueue)>0):
            self.cmdQueue.pop(0)()
    
    #shows homeScreen, deactivate lobbyScreen    
    def showHomeScreen(self):
        self.lobbyscreen.active=0
        self.home.active=1
    
    #shows homeScreen, removes gameNode and statistics
    def returnToHomeScreen(self):
        self.__rootNode.removeChild(self.__back)
        for stat in self.__playerStats:
            self.__rootNode.removeChild(stat)
            #stat = None
        self.__rootNode.removeChild(self.winnerNode)
        self.__rootNode.removeChild(self.statCounterNode)
        #self.__fieldNode = None
        #gc.collect()
        self.inStats = 0
    
    #stops game if all players are disconnected
    def interruptGame(self):
        if(self.inStats):
            return
        if(self.gamestate == 0):
            self.abortGame = 1
        else:
            self.__gameStateChanged("end")
    
    #shows lobbyScreen, deactivate homeScreen     
    def showLobbyScreen(self):
        self.lobbyscreen.active=1
        self.home.active=0
    
    #initialize WebSocket    
    def initializeWebSocket(self):
        log.startLogging(sys.stdout)##Create a logfile (not necessary)
        self.factory = WebSocketServerFactory("ws://localhost:9000", debug = False)
        self.factory.protocol = EchoServerProtocol ##assign our Protocol to send/receive Messages
        listenWS(self.factory)
        reactor.run(installSignalHandlers=0)#@UndefinedVariable##"installSignalHandlers=0" Necessary for Multithreading
    
    #counter to start game
    def counter(self):
        t = int(ub.lobbyscreen.timer.getTextAsDisplayed())
        print t
        if t > 0:
            t-=1
            ub.lobbyscreen.timer.text = str(t)
        elif t==0:
            ub.lobbyscreen.timer.text = "Spiel wird geladen..."
            ips.updateAll("start!")
            self.abortGame = 0
            self.gamestarted = 1
            self.cmdQueue.append(lambda:self.startGame())
            self.player.clearInterval(self.timer)
    
    #counter to return to homeScreen after statistics        
    def statCounter(self):
        t = ub.statCounterValue
        print t
        ub.statCounterNode.text = str(t)
        if t > 0:
            ub.statCounterValue -= 1   
        elif t==0:
            self.player.clearInterval(self.__statTimer)
            ips.disconnectAll()
            self.gamestarted = 0
            self.cmdQueue.append(lambda:self.returnToHomeScreen())
    
    #starts gameTimer    
    def startTimer(self):
        if(self.timer == None):
            self.timer = ub.player.setInterval(1000,self.counter)
    
    #stops gameTimer if there are not enough player anymore    
    def stopTimer(self):
        if(self.timer != None):
            self.player.clearInterval(self.timer)
        self.timer = None
    
    #starts statTimer    
    def startStatTimer(self):
        self.__statTimer = ub.player.setInterval(1000,self.statCounter)
    
    #starts game, creates fieldNode and staticNodes    
    def startGame(self):
        self.statCounterValue = 10
        self.__back = avg.RectNode(size = self.__size,  fillopacity=1, fillcolor="CCCCCC",  color="CCCCCC", parent=self.__rootNode)
        
        blockSize = (self.__size[1]/self.__fieldSize[1], self.__size[1]/self.__fieldSize[1])
        
        #prepearing users
        userlist = []
        for u in users.itervalues():
            if(u.getCharID != None):
                userlist.append(u)
        
        #init Game
        self.__fieldNode = field.FieldNode(userlist, self.__rootNode, self.__userChanged, self.__gameStateChanged, self.__gameEventHandler, fieldSize=self.__fieldSize, blockSize=blockSize)
        fieldSize = self.__fieldNode.getPixelSize()
        self.__sideBoardWidth = (self.__size[0]-fieldSize[0])/2
        self.__fieldNode.pos = (self.__sideBoardWidth, self.__topOffset)
        statsSize = (self.__sideBoardWidth,0)
        
        # init player stats 1-4
        self.__playerStats = []
        if(len(userlist)>0):
            self.__playerStats.append(playerStats.PlayerStats(userlist[0], parent = self.__rootNode, pos = (0,self.__topOffset), size = statsSize))
            self.__statsHeight = self.__playerStats[0].height
        if(len(userlist)>1):
            self.__playerStats.append(playerStats.PlayerStats(userlist[1], parent = self.__rootNode, pos = (self.__size[0]-statsSize[0],self.__topOffset), size = statsSize))
        if(len(userlist)>2):
            stat = playerStats.PlayerStats(userlist[2], parent = self.__rootNode, pos = (0,self.__size[1]-statsSize[1]), size = statsSize)
            #stat.size[1] = stat.height
            stat.pos = (0,self.__size[1]-stat.size[1])
            self.__playerStats.append(stat)
        if(len(userlist)>3):
            stat = playerStats.PlayerStats(userlist[3], parent = self.__rootNode, size = statsSize)
            #stat.size[1] = stat.height
            stat.pos = (self.__size[0]-statsSize[0],self.__size[1]-stat.size[1])
            self.__playerStats.append(stat)
            
        self.player.setTimeout(0, lambda: self.__fieldNode.countDown())
    
    #event Handler for user: powerups, dead    
    def __userChanged(self, user):
        self.__playerStats[user.getUserID()].updateUser(user)
        if(user.isDead()):
            for u in users:
                if(users[u].getName() == user.getName()):
                    ips.getConnectionForIp(u).sendMessage("client_death!")
            
    #event Handler for gameState    
    def __gameStateChanged(self, state):
        #game started
        if state == "start":
            self.gamestate = 1;
            # timeout for sudden death
            self.__suddenDeathTimeoutID = self.player.setTimeout(self.suddenDeathTimer, self.__fieldNode.startSuddenDeath)
            ips.updateAll("GO!");
            print "game started"
            #abort game if all users disconnected
            if(self.abortGame ==1):
                self.abortGame = 0
                self.__gameStateChanged("end")
        #game finished
        elif state == "end":
            self.gamestate = 0
            self.inStats = 1
            ips.updateAll("Stop!")
            #show statistic, winner for specific time
            self.statCounterNode = avg.WordsNode(parent = self.__rootNode, font="calibri", color="000000", variant="bold", letterspacing=2, fontsize=self.__size[1]/10, text="", pos=(self.__size[0]*1/16,self.__size[1]*7/8))
            self.winnerNode = avg.WordsNode(parent = self.__rootNode, font="calibri", alignment="center", color="000000", variant="bold", letterspacing=2, fontsize=self.__size[1]/10, text="Unentschieden!!!", size = (self.__size[0],50), pos=(self.__size[0]/2,20))
            self.winnerNode.active = 0
            count = 0
            for u in users.itervalues():
                if(u.isDead()!=1):
                    count +=1
                    self.winnerNode.text = u.getName()+" hat Gewonnen!!!!"
            if(count>1):
                self.winnerNode.text = "Unentschieden!!!"
            self.winnerNode.active = 1
            # prevent sudden death from execution if game ended in another way
            self.player.clearInterval(self.__suddenDeathTimeoutID)
            #remove field
            self.cmdQueue.append(lambda:self.__rootNode.removeChild(self.__fieldNode))
            #self.__fieldNode.active = 0
            #animate statistic to the middle of display
            new_pos = []
            new_pos.append((self.__size[0]/2-self.__sideBoardWidth,self.__size[1]/2-self.__statsHeight))
            new_pos.append((self.__size[0]/2,self.__size[1]/2-self.__statsHeight))
            new_pos.append((self.__size[0]/2-self.__sideBoardWidth,self.__size[1]/2))
            new_pos.append((self.__size[0]/2,self.__size[1]/2))
            for i in range(len(self.__playerStats)):
                animObj = avg.LinearAnim(self.__playerStats[i], "pos", 1500, self.__playerStats[i].pos, new_pos[i])
                animObj.start()
            
            ub.startStatTimer();
            print "game finished"
    
    #event handler for sudden_death, upgrades and placed bombs        
    def __gameEventHandler(self, event, ID=0):
        # sudden_death
        if(event == "sudden_death"):
            ips.updateAll("sudden_death!")
        # upgrade_collected:<UserID>
        if(event == "upgrade_collected"):
            for key in users:
                if(users[key].getUserID() == ID):
                    ips.getConnectionForIp(key).sendMessage("upgrade_collected!")
        # crash:<UserID>
        # bomb_placed:<UserID>
        if(event == "bomb_placed"):
            for key in users:
                if(users[key].getUserID() == ID):
                    ips.getConnectionForIp(key).sendMessage("bomb_placed!")
        # bomb_detonated
        #if(event == "bomb_detonated"):
            #ips.updateAll("bomb_detonated!")
            
        print event
    
    #key down from mobilephone for display   
    def k_down(self, userId, direction):
        if(self.gamestate):
            if(direction == "bomb"):
                self.__fieldNode.addBomb(userId)
            else:
                self.__fieldNode.movePlayer(userId, direction)
    
    #key up from mobilephone for display   
    def k_up(self, userId, direction):
        if(self.gamestate):
            self.__fieldNode.stopPlayer(userId, direction)
            
###THIS CLASS SIMPLY HOLDS THE CONNECTED CLIENT IPS####
class IPStorage():
    def __init__(self):##init
        self._ipList=dict({})
        self.__lock = threading.Lock()
        self.__maxPlayer = threading.Semaphore(4)
        
    def addNewClient(self,ip,connection): ##adds a new Client to the Dictionary
        self.__maxPlayer.acquire()
        self.__lock.acquire()
        self._ipList[ip]=connection
        users[ip] = user.User("",len(users),None)
        kickCounter[ip] = 0;
        if(len(self._ipList) == 1):
            ub.cmdQueue.append(lambda:ub.stopTimer())
            ub.cmdQueue.append(lambda:ub.lobbyscreen.resetTimer)
            ub.cmdQueue.append(lambda:ub.showLobbyScreen())
        if(len(self._ipList) >= ub.minPlayer):#normalerweise 2
            ub.cmdQueue.append(lambda:ub.startTimer())
        self.__lock.release()
    
    def dropConnection(self,ip):##removes Connection out of Dict
        self.__lock.acquire()
        if(users.has_key(ip)):
            del users[ip]
        if(ub.gamestarted == 0):
            i=0
            for u in users.itervalues():
                if(u.getUserID!=i):
                    u.setUserId(i)
                i+=1
            ub.cmdQueue.append(lambda:ub.lobbyscreen.updatePlayers(users))
        if(kickCounter.has_key(ip)):
            del kickCounter[ip]
        if(self._ipList.has_key(ip)):
            del self._ipList[ip]
        self.__maxPlayer.release()
        if(len(self._ipList) <2):
            ub.cmdQueue.append(lambda:ub.stopTimer())
            ub.cmdQueue.append(lambda:ub.lobbyscreen.resetTimer())
        if(len(self._ipList) == 0):
            ub.cmdQueue.append(lambda:ub.showHomeScreen())
            if(ub.gamestarted ==1):
                ub.cmdQueue.append(lambda:ub.interruptGame())
        self.__lock.release()
        
    def getAllCurrentConnections(self):#returns all currently active Connections
        return self._ipList
    
    def getNumberOfConnections(self):#returns number of all currently active Connections
        return len(self._ipList)
    
    def getConnectionForIp(self,ip):##returns a Connection to a Client with a certain IP
        return self._ipList[ip]
    
    def updateAll(self,msg): #sends Message to all connected Clients
        for key in self._ipList:
            self._ipList[key].sendMessage(msg)
            
    def disconnectAll(self): #disconnects all currently active Connections
        for key in self._ipList:
            self._ipList[key].sendMessage("Stop!")
            self._ipList[key].sendClose()
    
    def kickIp(self,ip): #kick a single ip
        self._ipList[ip].sendMessage("kicked!")
        self._ipList[ip].sendClose()
        
        

###WEBSOCKETPROTOCOL USED FOR COMMUNICATION####
class EchoServerProtocol(WebSocketServerProtocol):

    def onClose(self,wasClean,code,reason):
        print "Client left"
        if(ub.gamestarted == 0):
            if(users.has_key(self.peer.host)):
                ips.updateAll("Player left!"+users[self.peer.host].getName())#Update all
        ips.dropConnection(self.peer.host) ##Drop Connection out of IPStorage when Client disconnects
        
    def onOpen(self):
        ips.addNewClient(self.peer.host, self) ##adds current Connection and Client IP to the Storage
        if(ub.gamestarted==1):
            ips.kickIp(self.peer.host)
        #ips.updateAll("New Client with IP "+self.peer.host+" has joined")
              
    def onMessage(self, msg, binary):
        #print users
        #print "sending echo:", msg ##print incoming message
        ##self.sendMessage("Received: "+msg, binary)##send back message to initiating client

        #evaluate messages:
        parameter = msg.split("!")
        if (len(parameter) >0):
            command = parameter.pop(0)
        else:
            print("error")
            return
        
        #if(command == "test"):
        #    ips.kickIp(self.peer.host)
        if(users.has_key(self.peer.host)==0):
            ips.kickIp(self.peet.host)  
            return  
        if(command == "Hello Server"):##first message from Client, contains name and CharId
            if(ub.gamestarted == 1):
                return
            if (len(parameter) >0):
                attr = parameter.pop(0)
            else:
                print("error")
                return
            
            attributes = attr.split("#")
            if (len(attributes) >1):
                name = attributes.pop(0)
                charId = attributes.pop(0)
            else:
                print("error")
                return
            
            newNameFound = 0
            counter = 1
            while(newNameFound == 0):
                check = 1
                connectedUsers = ""
                for user in users.values(): ##checks if name already exist
                    if(user.getName() == name):
                        check = 0
                    if(user.getName() != None):
                        if(connectedUsers == ""):
                            connectedUsers += user.getName()
                        else:
                            connectedUsers += "#" + user.getName()
                if(check):
                    newNameFound = 1
                    users[self.peer.host].setName(name)
                    users[self.peer.host].setCharId(charId)
                    self.sendMessage("Hello Client!"+name+"&"+connectedUsers, binary)
                    ips.updateAll("New Player!"+name)
                    ub.cmdQueue.append(lambda:ub.lobbyscreen.updatePlayers(users))
                    print(name + "#" + charId)
                else:
                    #print("error")
                    #self.sendMessage("name not available", binary)
                    name = name + "("+str(counter)+")"
                    counter = counter +1
        
        #move comands from mobile phone        
        if(command == "kd"): ## KD=key down
            if(ub.gamestate ==1):
                if( users.has_key(self.peer.host)):
                    ub.cmdQueue.append(lambda:ub.k_down(users[self.peer.host].getUserID(), parameter.pop(0)))
        
        #move comands from mobile phone     
        if(command == "ku"): ## KU=key up
            if(ub.gamestate ==1):
                if( users.has_key(self.peer.host)):
                    ub.cmdQueue.append(lambda:ub.k_up(users[self.peer.host].getUserID(), parameter.pop(0)))
        
        #kick vote from mobile phone    
        if(command == "Vote Kick"):
            if(ub.gamestarted ==0):
                if( users.has_key(self.peer.host)):
                    name = parameter.pop()
                    for key in ips.getAllCurrentConnections():
                        if(users[key].getName() == name):
                            kickCounter[key] = kickCounter[key]+1
                            if(kickCounter[key] >= ub.minKickVotes):
                                ips.kickIp(key)
                
            
        
        
if __name__ == '__main__':
    users = OrderedDict() 
    kickCounter = dict({})
    ips=IPStorage()
    #ub = UbiBomb.start(resolution=(1024,768))
    ub = UbiBomb()
    ub.player.play()